/**
 * 地理编码转换工具
 * 支持Excel文件的地理编码批量转换
 */

const XLSX = require('xlsx');
const axios = require('axios');
const coordtransform = require('coordtransform');
const { MAP_PLATFORMS } = require('../config/app-config');
const FileHelper = require('../utils/file-helper');
const CacheManager = require('../utils/cache-manager');
const Logger = require('../utils/logger');

class GeocodingTool {
    constructor() {
        this.workbook = null;
        this.selectedSheets = [];
        this.selectedColumn = '';
        this.isProcessing = false;
        this.isPaused = false;
        this.processedData = new Map();
        
        // 初始化工具类
        this.cacheManager = new CacheManager();
        this.logger = new Logger('logContainer');
        
        // DOM元素引用
        this.elements = null;
        
        // 立即初始化，由主应用统一管理初始化时机
        setTimeout(() => this.init(), 100);
    }

    /**
     * 获取DOM元素引用
     * @returns {Object} DOM元素对象
     */
    getElements() {
        return {
            inputFile: document.getElementById('inputFile'),
            outputFile: document.getElementById('outputFile'),
            selectFileBtn: document.getElementById('selectFileBtn'),
            selectSaveBtn: document.getElementById('selectSaveBtn'),
            sheetSection: document.getElementById('sheetSection'),
            sheetList: document.getElementById('sheetList'),
            columnSection: document.getElementById('columnSection'),
            columnSelect: document.getElementById('columnSelect'),
            platformSelect: document.getElementById('platformSelect'),
            apiKey: document.getElementById('apiKey'),
            requestDelay: document.getElementById('requestDelay'),
            progressSection: document.getElementById('progressSection'),
            progressFill: document.getElementById('progressFill'),
            progressText: document.getElementById('progressText'),
            progressPercent: document.getElementById('progressPercent'),
            successCount: document.getElementById('successCount'),
            failCount: document.getElementById('failCount'),
            totalCount: document.getElementById('totalCount'),
            startBtn: document.getElementById('startBtn'),
            pauseBtn: document.getElementById('pauseBtn'),
            resumeBtn: document.getElementById('resumeBtn'),
            stopBtn: document.getElementById('stopBtn'),
            clearCacheBtn: document.getElementById('clearCacheBtn')
        };
    }

    /**
     * 初始化工具
     */
    init() {
        this.elements = this.getElements();
        this.bindEvents();
        this.updateApiKeyPlaceholder();
        this.logger.info('地理编码转换工具已初始化');
    }

    /**
     * 绑定事件监听器
     */
    bindEvents() {
        const { elements } = this;

        // 文件选择事件
        elements.selectFileBtn?.addEventListener('click', () => this.selectInputFile());
        elements.selectSaveBtn?.addEventListener('click', () => this.selectOutputPath());

        // 平台切换事件
        elements.platformSelect?.addEventListener('change', () => this.updateApiKeyPlaceholder());

        // 控制按钮事件
        elements.startBtn?.addEventListener('click', () => this.startProcessing());
        elements.pauseBtn?.addEventListener('click', () => this.pauseProcessing());
        elements.resumeBtn?.addEventListener('click', () => this.resumeProcessing());
        elements.stopBtn?.addEventListener('click', () => this.stopProcessing());
        elements.clearCacheBtn?.addEventListener('click', () => this.clearCache());

        // 表单变化事件
        elements.columnSelect?.addEventListener('change', () => this.updateStartButton());
        elements.apiKey?.addEventListener('input', () => this.updateStartButton());
    }

    /**
     * 更新API Key占位符
     */
    updateApiKeyPlaceholder() {
        const platform = this.elements.platformSelect?.value;
        const config = MAP_PLATFORMS[platform];
        
        if (config && this.elements.apiKey) {
            this.elements.apiKey.value = config.defaultKey;
            this.elements.apiKey.placeholder = `请输入${config.name} API Key`;
            this.logger.info(`已切换到${config.name}平台`);
        }
    }

    /**
     * 选择输入文件
     */
    async selectInputFile() {
        try {
            const filePath = await FileHelper.selectExcelFile();
            
            if (filePath) {
                this.elements.inputFile.value = filePath;
                await this.loadExcelFile(filePath);
                this.logger.info(`已选择文件: ${FileHelper.getFileName(filePath)}`);
            }
        } catch (error) {
            this.logger.error(`选择文件失败: ${error.message}`);
        }
    }

    /**
     * 选择输出路径
     */
    async selectOutputPath() {
        try {
            const filePath = await FileHelper.selectSavePath({
                defaultPath: 'geocoded_result.xlsx'
            });
            
            if (filePath) {
                this.elements.outputFile.value = filePath;
                this.logger.info(`已选择保存路径: ${FileHelper.getFileName(filePath)}`);
                this.updateStartButton();
            }
        } catch (error) {
            this.logger.error(`选择保存路径失败: ${error.message}`);
        }
    }

    /**
     * 加载Excel文件
     * @param {string} filePath - 文件路径
     */
    async loadExcelFile(filePath) {
        try {
            this.workbook = XLSX.readFile(filePath);
            const sheetNames = this.workbook.SheetNames;
            
            if (sheetNames.length > 1) {
                this.showSheetSelection(sheetNames);
            } else {
                this.selectedSheets = [sheetNames[0]];
                this.loadColumns(sheetNames[0]);
            }
            
            this.logger.info(`成功加载Excel文件，包含 ${sheetNames.length} 个工作表`);
        } catch (error) {
            this.logger.error(`加载Excel文件失败: ${error.message}`);
        }
    }

    /**
     * 显示工作表选择界面
     * @param {Array} sheetNames - 工作表名称数组
     */
    showSheetSelection(sheetNames) {
        this.elements.sheetSection.style.display = 'block';
        this.elements.sheetList.innerHTML = '';
        
        sheetNames.forEach(sheetName => {
            const div = document.createElement('div');
            div.className = 'sheet-item';
            div.innerHTML = `
                <label>
                    <input type="checkbox" value="${sheetName}" checked>
                    ${sheetName}
                </label>
            `;
            this.elements.sheetList.appendChild(div);
        });
        
        // 添加确认按钮
        const confirmBtn = document.createElement('button');
        confirmBtn.textContent = '确认选择';
        confirmBtn.className = 'btn btn-primary';
        confirmBtn.onclick = () => this.confirmSheetSelection();
        this.elements.sheetList.appendChild(confirmBtn);
    }

    /**
     * 确认工作表选择
     */
    confirmSheetSelection() {
        const checkboxes = this.elements.sheetList.querySelectorAll('input[type="checkbox"]:checked');
        this.selectedSheets = Array.from(checkboxes).map(cb => cb.value);
        
        if (this.selectedSheets.length === 0) {
            this.logger.error('请至少选择一个工作表');
            return;
        }
        
        this.logger.info(`已选择 ${this.selectedSheets.length} 个工作表: ${this.selectedSheets.join(', ')}`);
        this.loadColumns(this.selectedSheets[0]);
    }

    /**
     * 加载列信息
     * @param {string} sheetName - 工作表名称
     */
    loadColumns(sheetName) {
        try {
            const worksheet = this.workbook.Sheets[sheetName];
            const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
            
            if (jsonData.length === 0) {
                this.logger.error('工作表为空');
                return;
            }
            
            const headers = jsonData[0];
            this.elements.columnSelect.innerHTML = '<option value="">请选择列</option>';
            
            headers.forEach((header, index) => {
                if (header) {
                    const option = document.createElement('option');
                    option.value = index;
                    option.textContent = header;
                    this.elements.columnSelect.appendChild(option);
                }
            });
            
            this.elements.columnSection.style.display = 'block';
            this.logger.info(`已加载列信息，共 ${headers.length} 列`);
        } catch (error) {
            this.logger.error(`加载列信息失败: ${error.message}`);
        }
    }

    /**
     * 更新开始按钮状态
     */
    updateStartButton() {
        const hasInputFile = this.elements.inputFile?.value.trim() !== '';
        const hasOutputFile = this.elements.outputFile?.value.trim() !== '';
        const hasColumn = this.elements.columnSelect?.value !== '';
        const hasApiKey = this.elements.apiKey?.value.trim() !== '';
        
        if (this.elements.startBtn) {
            this.elements.startBtn.disabled = !(hasInputFile && hasOutputFile && hasColumn && hasApiKey);
        }
    }

    /**
     * 开始处理
     */
    async startProcessing() {
        if (this.isProcessing) return;
        
        this.isProcessing = true;
        this.isPaused = false;
        
        this.updateControlButtons();
        this.elements.progressSection.style.display = 'block';
        
        try {
            await this.processAllSheets();
            this.logger.success('所有工作表处理完成！');
        } catch (error) {
            this.logger.error(`处理失败: ${error.message}`);
        } finally {
            this.isProcessing = false;
            this.updateControlButtons();
        }
    }

    /**
     * 处理所有工作表
     */
    async processAllSheets() {
        const platform = this.elements.platformSelect.value;
        const apiKey = this.elements.apiKey.value.trim();
        const delay = parseInt(this.elements.requestDelay.value) || 100;
        const columnIndex = parseInt(this.elements.columnSelect.value);
        
        // 初始化缓存
        const inputFileName = FileHelper.getFileName(this.elements.inputFile.value);
        const cacheKey = this.cacheManager.generateCacheKey(inputFileName, platform);
        this.loadCache(cacheKey);
        
        for (const sheetName of this.selectedSheets) {
            if (!this.isProcessing) break;
            
            this.logger.info(`开始处理工作表: ${sheetName}`);
            await this.processSheet(sheetName, platform, apiKey, delay, columnIndex);
            this.logger.info(`工作表 ${sheetName} 处理完成`);
        }
        
        if (this.isProcessing) {
            await this.saveResults();
            this.cacheManager.delete(cacheKey);
        }
    }

    /**
     * 处理单个工作表
     * @param {string} sheetName - 工作表名称
     * @param {string} platform - 地图平台
     * @param {string} apiKey - API密钥
     * @param {number} delay - 请求延迟
     * @param {number} columnIndex - 列索引
     */
    async processSheet(sheetName, platform, apiKey, delay, columnIndex) {
        const worksheet = this.workbook.Sheets[sheetName];
        const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
        
        if (jsonData.length <= 1) {
            this.logger.warning(`工作表 ${sheetName} 没有数据行`);
            return;
        }
        
        const headers = jsonData[0];
        const dataRows = jsonData.slice(1);
        
        // 添加新列标题
        this.addCoordinateColumns(headers, platform);
        
        // 更新进度信息
        this.updateProgressInfo(0, dataRows.length, 0, 0);
        
        let successCount = 0;
        let failCount = 0;
        
        for (let i = 0; i < dataRows.length; i++) {
            if (!this.isProcessing) break;
            
            // 处理暂停状态
            while (this.isPaused) {
                await this.sleep(100);
            }
            
            const row = dataRows[i];
            const address = row[columnIndex];
            
            if (!address || address.toString().trim() === '') {
                failCount++;
                continue;
            }
            
            try {
                const result = await this.processAddress(address, platform, apiKey, delay);
                
                if (result.success) {
                    this.addCoordinatesToRow(row, headers, result, platform);
                    successCount++;
                    this.logger.info(`成功: ${address} -> (${result.lng || result.gcj02_lng}, ${result.lat || result.gcj02_lat})`);
                } else {
                    failCount++;
                    this.logger.error(`失败: ${address} - ${result.error}`);
                }
            } catch (error) {
                failCount++;
                this.logger.error(`处理失败: ${address} - ${error.message}`);
            }
            
            // 更新进度
            this.updateProgress(i + 1, dataRows.length, successCount, failCount);
        }
        
        // 更新工作表数据
        const newData = [headers, ...dataRows];
        const newWorksheet = XLSX.utils.aoa_to_sheet(newData);
        this.workbook.Sheets[sheetName] = newWorksheet;
    }

    /**
     * 处理单个地址
     * @param {string} address - 地址
     * @param {string} platform - 平台
     * @param {string} apiKey - API密钥
     * @param {number} delay - 延迟
     * @returns {Object} 处理结果
     */
    async processAddress(address, platform, apiKey, delay) {
        const cacheKey = `${platform}_${address}`;
        
        // 检查缓存
        if (this.processedData.has(cacheKey)) {
            this.logger.info(`使用缓存: ${address}`);
            return this.processedData.get(cacheKey);
        }
        
        // 调用地理编码API
        const result = await this.geocodeAddress(address, platform, apiKey);
        
        // 缓存结果
        if (result.success) {
            this.processedData.set(cacheKey, result);
        }
        
        // 请求延迟
        await this.sleep(delay);
        
        return result;
    }

    /**
     * 地理编码API调用
     * @param {string} address - 地址
     * @param {string} platform - 平台
     * @param {string} apiKey - API密钥
     * @returns {Object} 编码结果
     */
    async geocodeAddress(address, platform, apiKey) {
        try {
            const config = MAP_PLATFORMS[platform];
            if (!config) {
                throw new Error(`不支持的平台: ${platform}`);
            }

            switch (platform) {
                case 'amap':
                    return await this.geocodeWithAmap(address, apiKey);
                case 'tencent':
                    return await this.geocodeWithTencent(address, apiKey);
                case 'tianditu':
                    return await this.geocodeWithTianditu(address, apiKey);
                default:
                    throw new Error(`不支持的平台: ${platform}`);
            }
        } catch (error) {
            return { success: false, error: error.message };
        }
    }

    /**
     * 高德地图地理编码
     * @param {string} address - 地址
     * @param {string} apiKey - API密钥
     * @returns {Object} 编码结果
     */
    async geocodeWithAmap(address, apiKey) {
        const config = MAP_PLATFORMS.amap;
        const response = await axios.get(config.baseUrl, {
            params: {
                key: apiKey,
                address: address,
                output: 'json'
            },
            timeout: config.timeout
        });
        
        if (response.data.status === '1' && response.data.geocodes && response.data.geocodes.length > 0) {
            const location = response.data.geocodes[0].location.split(',');
            const gcj02_lng = parseFloat(location[0]);
            const gcj02_lat = parseFloat(location[1]);
            
            // 转换为WGS84坐标
            const [wgs84_lng, wgs84_lat] = coordtransform.gcj02towgs84(gcj02_lng, gcj02_lat);
            
            return {
                success: true,
                gcj02_lng,
                gcj02_lat,
                wgs84_lng,
                wgs84_lat
            };
        } else {
            throw new Error(response.data.info || '地理编码失败');
        }
    }

    /**
     * 腾讯位置服务地理编码
     * @param {string} address - 地址
     * @param {string} apiKey - API密钥
     * @returns {Object} 编码结果
     */
    async geocodeWithTencent(address, apiKey) {
        const config = MAP_PLATFORMS.tencent;
        const response = await axios.get(config.baseUrl, {
            params: {
                key: apiKey,
                address: address,
                output: 'json'
            },
            timeout: config.timeout
        });
        
        if (response.data.status === 0 && response.data.result && response.data.result.location) {
            const location = response.data.result.location;
            const gcj02_lng = location.lng;
            const gcj02_lat = location.lat;
            
            // 转换为WGS84坐标
            const [wgs84_lng, wgs84_lat] = coordtransform.gcj02towgs84(gcj02_lng, gcj02_lat);
            
            return {
                success: true,
                gcj02_lng,
                gcj02_lat,
                wgs84_lng,
                wgs84_lat
            };
        } else {
            const errorMsg = response.data.message || '地理编码失败';
            throw new Error(errorMsg);
        }
    }

    /**
     * 天地图地理编码
     * @param {string} address - 地址
     * @param {string} apiKey - API密钥
     * @returns {Object} 编码结果
     */
    async geocodeWithTianditu(address, apiKey) {
        const config = MAP_PLATFORMS.tianditu;
        const response = await axios.get(config.baseUrl, {
            params: {
                tk: apiKey,
                ds: JSON.stringify({
                    keyWord: address
                })
            },
            timeout: config.timeout
        });
        
        if (response.data.status === '0' && response.data.location) {
            return {
                success: true,
                lng: response.data.location.lon,
                lat: response.data.location.lat
            };
        } else {
            throw new Error(response.data.msg || '地理编码失败');
        }
    }

    /**
     * 添加坐标列
     * @param {Array} headers - 表头数组
     * @param {string} platform - 平台
     */
    addCoordinateColumns(headers, platform) {
        if (platform === 'tianditu') {
            if (!headers.includes('wgs84_x')) headers.push('wgs84_x');
            if (!headers.includes('wgs84_y')) headers.push('wgs84_y');
        } else {
            if (!headers.includes('gcj02_x')) headers.push('gcj02_x');
            if (!headers.includes('gcj02_y')) headers.push('gcj02_y');
            if (!headers.includes('wgs84_x')) headers.push('wgs84_x');
            if (!headers.includes('wgs84_y')) headers.push('wgs84_y');
        }
    }

    /**
     * 向行中添加坐标数据
     * @param {Array} row - 数据行
     * @param {Array} headers - 表头
     * @param {Object} result - 地理编码结果
     * @param {string} platform - 平台
     */
    addCoordinatesToRow(row, headers, result, platform) {
        if (platform === 'tianditu') {
            const wgs84XIndex = headers.indexOf('wgs84_x');
            const wgs84YIndex = headers.indexOf('wgs84_y');
            row[wgs84XIndex] = result.lng;
            row[wgs84YIndex] = result.lat;
        } else {
            const gcj02XIndex = headers.indexOf('gcj02_x');
            const gcj02YIndex = headers.indexOf('gcj02_y');
            const wgs84XIndex = headers.indexOf('wgs84_x');
            const wgs84YIndex = headers.indexOf('wgs84_y');
            
            row[gcj02XIndex] = result.gcj02_lng;
            row[gcj02YIndex] = result.gcj02_lat;
            row[wgs84XIndex] = result.wgs84_lng;
            row[wgs84YIndex] = result.wgs84_lat;
        }
    }

    /**
     * 更新进度信息
     * @param {number} current - 当前进度
     * @param {number} total - 总数
     * @param {number} success - 成功数
     * @param {number} fail - 失败数
     */
    updateProgressInfo(current, total, success, fail) {
        this.elements.totalCount.textContent = total;
        this.elements.successCount.textContent = success;
        this.elements.failCount.textContent = fail;
    }

    /**
     * 更新进度条
     * @param {number} current - 当前进度
     * @param {number} total - 总数
     * @param {number} success - 成功数
     * @param {number} fail - 失败数
     */
    updateProgress(current, total, success, fail) {
        const progress = (current / total) * 100;
        this.elements.progressFill.style.width = `${progress}%`;
        this.elements.progressPercent.textContent = `${Math.round(progress)}%`;
        this.elements.progressText.textContent = `处理中... ${current}/${total}`;
        this.elements.successCount.textContent = success;
        this.elements.failCount.textContent = fail;
    }

    /**
     * 更新控制按钮状态
     */
    updateControlButtons() {
        this.elements.startBtn.disabled = this.isProcessing;
        this.elements.pauseBtn.disabled = !this.isProcessing || this.isPaused;
        this.elements.resumeBtn.disabled = !this.isProcessing || !this.isPaused;
        this.elements.stopBtn.disabled = !this.isProcessing;
    }

    /**
     * 暂停处理
     */
    pauseProcessing() {
        this.isPaused = true;
        this.updateControlButtons();
        this.elements.progressText.textContent = '已暂停';
        this.logger.warning('处理已暂停');
    }

    /**
     * 继续处理
     */
    resumeProcessing() {
        this.isPaused = false;
        this.updateControlButtons();
        this.elements.progressText.textContent = '处理中...';
        this.logger.info('处理已继续');
    }

    /**
     * 停止处理
     */
    stopProcessing() {
        this.isProcessing = false;
        this.isPaused = false;
        this.updateControlButtons();
        this.elements.progressText.textContent = '已停止';
        this.logger.warning('处理已停止');
    }

    /**
     * 保存结果
     */
    async saveResults() {
        try {
            const outputPath = this.elements.outputFile.value;
            XLSX.writeFile(this.workbook, outputPath);
            this.logger.success(`结果已保存到: ${outputPath}`);
        } catch (error) {
            this.logger.error(`保存失败: ${error.message}`);
        }
    }

    /**
     * 加载缓存
     * @param {string} cacheKey - 缓存键
     */
    loadCache(cacheKey) {
        const cacheData = this.cacheManager.load(cacheKey);
        if (cacheData) {
            this.processedData = new Map(Object.entries(cacheData));
            this.logger.info(`已加载缓存，包含 ${this.processedData.size} 条记录`);
        }
    }

    /**
     * 保存缓存
     * @param {string} cacheKey - 缓存键
     */
    saveCache(cacheKey) {
        const cacheData = Object.fromEntries(this.processedData);
        this.cacheManager.save(cacheKey, cacheData);
    }

    /**
     * 清除缓存
     */
    clearCache() {
        this.cacheManager.clear();
        this.processedData.clear();
        this.logger.success('缓存已清除');
    }

    /**
     * 延迟函数
     * @param {number} ms - 延迟毫秒数
     * @returns {Promise} Promise对象
     */
    sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
}

module.exports = GeocodingTool;